#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#include <stdint.h>

#define DEV_NMAE "/dev/gpiochip0"

#define HELLO_DBG
#ifdef HELLO_DBG
#define hello_dbg(fmt, ...) printf("[%s][%d]" fmt "\r\n", __func__, __LINE__, ##__VA_ARGS__)
#else
#define hello_dbg(fmt, ...)
#endif

int main(int argc, char *argv[])
{
    int fd;
    int value;
    int ret;
    unsigned int gpio_num;

    hello_dbg("%s, %s", __DATE__, __TIME__);

    if (argc != 3)
    {
        hello_dbg("./test_hello_nenopi_led1.bin <gpio_num> <on|off|read|test>");
        return -1;
    }

    gpio_num = strtoul(argv[1], NULL, 10);
    hello_dbg("gpio_num:%u", gpio_num);

    if (strcmp(argv[2], "on") == 0)
    {
        value = 0;
    }
    else if (strcmp(argv[2], "off") == 0)
    {
        value = 1;
    }
    else if (strcmp(argv[2], "read") == 0)
    {
        value = 2;
    }
    else if (strcmp(argv[2], "test") == 0)
    {
        value = 3;
    }
    else
    {
        hello_dbg("./test_hello_nenopi_led1.bin <gpio_num> <on|off|read|test>");
        return -1;
    }

    fd = open(DEV_NMAE, O_RDWR);
    if (fd < 0)
    {
        hello_dbg("open %s error, erron:%d:%s", DEV_NMAE, errno, strerror(errno));
        return -1;
    }

    struct gpio_v2_line_request req;

    memset(&req, 0, sizeof(req));
    req.offsets[0] = gpio_num;
    req.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
    strcpy(req.consumer, "gpio_test");
    req.num_lines = 1;

    ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
    if (ret == -1)
    {
        hello_dbg("Failed to issue GPIO_V2_GET_LINE_IOCTL (%d), %s", ret, strerror(errno));
        close(fd);
        return -errno;
    }
    close(fd);

    struct gpio_v2_line_values values;
    values.mask = 0;
    values.bits = 0;

    if (value == 0 || value == 1)
    {
        values.mask = 1;
        values.bits = value == 0 ? 1 : 0;
        ret = ioctl(req.fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
        if (ret == -1)
        {
            hello_dbg("Failed to issue GPIO_V2_LINE_SET_VALUES_IOCTL (%d), %s", ret, strerror(errno));
            close(req.fd);
            return -errno;
        }
    }
    else if (value == 2)
    {
        values.mask = 1;
        values.bits = 0;
        ret = ioctl(req.fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
        if (ret == -1)
        {
            hello_dbg("Failed to issue GPIO_V2_LINE_GET_VALUES_IOCTL (%d), %s", ret, strerror(errno));
            close(req.fd);
            return -errno;
        }
        hello_dbg("read gpio:%llu", values.bits);
    }
    else if (value == 3)
    {

        uint8_t count = 0;
        uint8_t bits = 0;
        while (count++ < 5)
        {
            // on;
            bits ^= 1;
            values.mask = 1;
            values.bits = bits;
            hello_dbg("set gpio, mask:%llu, bits:%llu", values.mask, values.bits);
            ret = ioctl(req.fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
            if (ret == -1)
            {
                hello_dbg("Failed to issue GPIO_V2_LINE_SET_VALUES_IOCTL (%d), %s", ret, strerror(errno));
                close(req.fd);
                return -errno;
            }

            sleep(1);

            values.mask = 1;
            values.bits = 0;
            ret = ioctl(req.fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
            if (ret == -1)
            {
                hello_dbg("Failed to issue GPIO_V2_LINE_GET_VALUES_IOCTL (%d), %s", ret, strerror(errno));
                close(req.fd);
                return -errno;
            }
            hello_dbg("read gpio:%llu", values.bits);

            sleep(2);
        }
    }

    close(req.fd);

    return 0;
}
